﻿using SdmxMl.Structure;
using System;
using System.Collections.Generic;

namespace SdmxMl.Data
{

    #region Enum CSV representations

    // Enumerate CSV Measures representations
    public enum CsvModel
    {
        /// <summary>All measures presented in different columns</summary>
        LinearModel,

        /// <summary>All secondary measures presented in Observation column</summary>
        ColumnModel
    }
    #endregion


    /// <summary>Helper class for attaching metadata to CSV header </summary>
    public class CsvDataHeader
    {
        #region internal members
        private List<string> headerFields;

        #endregion

        #region Properties

        public CsvModel Model { get; private set; }
        public HeaderColDictionary DicoHeadCol { get; private set; }
        public List<DimensionType> MeasureDimensionList { get; private set; }

        public List<string> ListDsdColNotInCsv { get; private set; }
        #endregion

        #region Constructor

        public CsvDataHeader(KeyFamilyType dsd, List<string> headerFld, RefVarHelper refVarHelper)
        {
            headerFields = headerFld;
            DicoHeadCol = new HeaderColDictionary();
            ListDsdColNotInCsv = new List<string>();

            if (dsd != null && headerFields != null && refVarHelper != null)
            {
                List<BaseDimensionType> lbd = refVarHelper.PrepareVarList(dsd, false);
                List<BaseDimensionType> FlagNotPresent = new List<BaseDimensionType>();
                List<AttributeType> FlagAssigned = new List<AttributeType>();
                Model = DetermineCsvModel(dsd);
                foreach (BaseDimensionType bd in lbd)
                {
                    AttributeType att = bd as AttributeType;
                    int index = headerFields.IndexOf(bd.ConceptInfo.Id.ToUpper());
                    if (index >= 0)
                    {
                        if (DicoHeadCol.ContainsKey(index))
                            throw new ApplicationException("Invalid index " + index + " detected for " + bd.ConceptInfo.Id + ". Please checks DSD and CSV header for duplicate ID");
                        DicoHeadCol.Add(index, new CsvMetaHeaderCol(bd, refVarHelper));
                    }
                    // a flag not in header (add its codelist of quality)
                    else if (bd.ConceptInfo.Id.EndsWith("_F"))
                    {
                        if (att != null && att.AttachmentMeasureList.Count > 0 && att.AttachmentLevel == AttributeType.AttachmentLevelType.Observation)
                            FlagAssigned.Add(att);
                        else
                            FlagNotPresent.Add(bd);
                    }
                    // Attribute distributed to TODO:
                    else if (att != null && att.AttachmentMeasureList.Count > 0 && att.AttachmentLevel == AttributeType.AttachmentLevelType.Observation)
                    {
                        FlagAssigned.Add(att);
                    }
                    else
                        ListDsdColNotInCsv.Add(bd.ConceptInfo.Id.ToUpper());
                }

                // Treat quality for nonpresent flags
                foreach (BaseDimensionType bd in FlagNotPresent)
                {
                    CsvMetaHeaderCol metaHead = DicoHeadCol.GetMetaHeadLinkedToFlag(bd.ConceptInfo.Id);
                    if (metaHead != null)
                        metaHead.UpdateQuality(bd, refVarHelper);
                }

                // Attribute with assignations
                foreach (AttributeType attAssign in FlagAssigned)
                {
                    foreach (string dimId in attAssign.AttachmentMeasureList)
                    {
                        CsvMetaHeaderCol metaHead = DicoHeadCol.GetMetaHeadAssigned(dimId);
                        if (metaHead != null)
                            metaHead.UpdateQuality(attAssign, refVarHelper);
                    }
                }
                    


                if (Model == CsvModel.ColumnModel)
                {
                    //Record meta info on columns matching DSD
                    foreach (string headCol in headerFields)
                    {
                        /*
                        foreach (BaseDimensionType bd in lbd)
                        {
                            if (bd.ConceptInfo.Id.ToUpper() == headCol)
                            {
                                DicoHeadCol.Add(headerFields.IndexOf(headCol), new CsvMetaHeaderCol(bd, refVarHelper));
                                break;
                            }
                        }
                         * */

                        // register Obs column && dim measures columns
                        foreach (DimensionType dim in MeasureDimensionList)
                        {
                            if (headCol == dim.ConceptInfo.Id.ToUpper())
                                DicoHeadCol.Add(headerFields.IndexOf(headCol), new CsvMetaHeaderCol(dim, refVarHelper));
                        }

                        if (headCol == dsd.PrimaryMeasure.ConceptInfo.Id.ToUpper())
                            DicoHeadCol.Add(headerFields.IndexOf(headCol), new CsvMetaHeaderCol(dsd.PrimaryMeasure, refVarHelper));

                    }
                }

            }
        }


        #endregion

        CsvModel DetermineCsvModel(KeyFamilyType dsd)
        {
            CsvModel aModel = CsvModel.LinearModel;
            if (dsd.CrossSectionalMeasureList.Count > 1 && 
                headerFields.Contains(dsd.PrimaryMeasure.ConceptInfo.Id.ToUpper()) &&
                dsd.HasMeasureDimension)
            {
                // store measures 
                MeasureDimensionList = dsd.MeasureDimensionList;
                aModel = CsvModel.ColumnModel;
            }

            return aModel;
        }
    }

    public class HeaderColDictionary : Dictionary<int, CsvMetaHeaderCol>
    {
        public CsvMetaHeaderCol GetMetaHeadLinkedToFlag(string flagId)
        {
            string conceptId = flagId.Substring(0, flagId.Length - 2);
            foreach (CsvMetaHeaderCol metaHead in this.Values)
                if (metaHead.Concept.Id == conceptId)
                    return metaHead;
            return null;
        }

        public CsvMetaHeaderCol GetMetaHeadAssigned(string conceptId)
        {
            foreach (CsvMetaHeaderCol metaHead in this.Values)
                if (metaHead.Concept.Id == conceptId)
                    return metaHead;
            return null;
        }
    }
}
